package controller

import (
	"errors"
	"fmt"
	"strings"

	"gitee.com/liukexing/isrn/model"
	"gitee.com/liukexing/isrn/router"
	"github.com/orcaman/concurrent-map"
)

var (
	actionmap map[string]ActionHandler
	nodes     *cmap.ConcurrentMap
	reqmaps   *cmap.ConcurrentMap
)

type ActionHandler func(data []byte) ([]byte, error)

func init() {
	actionmap = make(map[string]ActionHandler)
	actionmap["keyswap"] = SwapAction
	AddMap((&TestController{}).ActionMapping())
}

func AddMap(Mapping map[string]ActionHandler) {
	for k, v := range Mapping {
		actionmap[k] = v
	}
}

func SetNodes(n *cmap.ConcurrentMap, r *cmap.ConcurrentMap) {
	nodes = n
	reqmaps = r
}

func Action(data *model.TranInfo, sender *router.NodeInfo) {
	if data.Encrypt {
		requestdata, err := sender.Key.Decrypt(data.Data)
		if err != nil {
			response(data.ReqNum, nil, err, sender)
			return
		}
		data.Data = requestdata
	}
	if data.Action == "response" || data.Action == "error" {
		//fmt.Println(string(data.Data))
		if info, ok := reqmaps.Get(string(data.ReqNum)); ok {
			res := info.(*model.ResponseInfo)
			if res.SignCheck {
				ok, err := sender.Key.Verify(data.Data, data.Sign)
				if err != nil {
					fmt.Println("Response Err:", err)
					return
				}
				if !ok {
					fmt.Println("Response Err:Sign Check Failed")
					return
				}
			}
			err := res.Handle(data, res.Receiver)
			if err != nil {
				fmt.Println("Response Err:", err)
			}
			reqmaps.Remove(string(data.ReqNum))
		}
		return
	}
	if data.Action != "keyswap" {
		ok, err := sender.Key.Verify(data.Data, data.Sign)
		if !ok {
			response(data.ReqNum, nil, errors.New("Verify Check failed"), sender)
			return
		}
		if err != nil {
			response(data.ReqNum, nil, err, sender)
			return
		}
	}

	if af, ok := actionmap[data.Action]; ok {
		responsemsg, err := af(data.Data)
		if err != nil {
			response(data.ReqNum, nil, err, sender)
			return
		}
		if data.Action == "keyswap" {
			ok, err := sender.Key.Verify(data.Data, data.Sign)
			if !ok {
				response(data.ReqNum, nil, errors.New("Verify Check failed"), sender)
				return
			}
			if err != nil {
				response(data.ReqNum, nil, err, sender)
				return
			}
		}
		response(data.ReqNum, responsemsg, nil, sender)
	} else {
		response(data.ReqNum, nil, errors.New("Action Not Find"), sender)
		return
	}
}

func response(reqnum, data []byte, errinfo error, sender *router.NodeInfo) {
	tranres := &model.TranInfo{}
	tranres.ReqNum = reqnum
	if errinfo != nil {
		tranres.Action = "error"
		tranres.Encrypt = false
		tranres.Data = []byte(errinfo.Error())
		sn, err := sender.Key.Sign(tranres.Data)
		if err != nil {
			fmt.Println("response Err:", err)
			return
		}
		tranres.Sign = sn
	} else {
		tranres.Action = "response"
		tranres.Data = data
		edata, err := sender.Key.Encrypt(data)
		if err != nil {
			response(reqnum, data, err, sender)
			return
		} else {
			tranres.Encrypt = true
			tranres.Data = edata
		}
		sn, err := sender.Key.Sign(data)
		if err != nil {
			fmt.Println("response Err:", err)
			return
		}
		tranres.Sign = sn
	}
	if len(sender.RouList) > 0 {
		router.TransSend(sender.Peerid, strings.Split(sender.RouList[0].Path, ","), tranres)
	}
}

func SwapAction(data []byte) ([]byte, error) {
	swapinfo := &model.KeySwapInfo{}
	_, err := swapinfo.GetData(data)
	if err != nil {
		return nil, err
	}

	if node, ok := nodes.Get(swapinfo.NodeId); ok {
		key := &node.(*router.NodeInfo).Key
		pk, err := key.UnmarshalPk(swapinfo.Pk)
		if err != nil {
			return nil, err
		}
		rpk, err := key.UnmarshalRpk(swapinfo.Rpk)
		if err != nil {
			return nil, err
		}
		key.Pk = pk
		key.Rpk = rpk
		myswap, err := key.BuildSwapInfo("Response")
		if err != nil {
			return nil, err
		}
		return myswap.GetByte()
	} else {
		return nil, errors.New("peerid not find in nodes")
	}
}
